package licode.unisop.authcenter.application.executor;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import licode.unisop.authcenter.application.info.*;
import licode.unisop.authcenter.exception.CenterErrorCode;
import licode.unisop.platform.client.api.SiPlatformQueryService;
import licode.unisop.platform.client.api.SiPlatformService;
import licode.unisop.platform.client.info.*;
import licode.unisop.provider.conf.SiAssetState;
import licode.unisop.provider.info.*;
import licode.unisop.provider.vo.*;
import licode.unisop.realm.client.api.SiRealmQueryService;
import licode.unisop.realm.client.api.SiRealmService;
import licode.unisop.realm.client.api.SiUserQueryService;
import licode.unisop.realm.client.api.SiUserService;
import licode.unisop.realm.client.dto.SiRealmDTO;
import licode.unisop.realm.client.info.SiRealmAttrRemoveIn;
import licode.unisop.realm.client.info.SiRealmGroupAttrRemoveIn;
import licode.unisop.realm.client.info.SiUserAttrRemoveIn;
import licode.unisop.types.AuthConst;
import licode.unisop.types.dp.UserAuthIn;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 定义统一认证平台中心资源相关接口
 *
 * @author licode
 */
@Service
public class CenterAssetExecutor {
    @Resource
    private SiPlatformService siPlatformService;

    @Resource
    private SiPlatformQueryService siPlatformQueryService;

    @Resource
    private SiRealmService siRealmService;

    @Resource
    private SiRealmQueryService siRealmQueryService;

    @Resource
    private SiUserService siUserService;

    @Resource
    private SiUserQueryService siUserQueryService;

    public void save(UserAuthIn authIn, PlatAssetSaveIn asset) {
        if ("REALM".equals(asset.getServer())) {
            saveRealmAsset(authIn, asset);
        } else {
            savePlatformAsset(authIn, asset);
        }
    }

    private void saveRealmAsset(UserAuthIn authIn, PlatAssetSaveIn asset) {
        siUserService.saveUserGroup(UserGroupSaveIn.builder()
                .realmId(asset.getTargetId())
                .group(SiUserGroup.builder()
                        .id(asset.getAsset().getId())
                        .name(asset.getAsset().getName())
                        .code(asset.getAsset().getCode())
                        .level(asset.getAsset().getLevel())
                        .state(null == asset.getAsset().getState() ?
                                SiAssetState.AT_ACTIVATE : asset.getAsset().getState())
                        .build())
                .build());
    }

    private void savePlatformAsset(UserAuthIn authIn, PlatAssetSaveIn asset) {
        SiAssetBrief brief = BeanUtil.copyProperties(asset.getAsset(), SiAssetBrief.class);

        checkAsset(brief);

        siPlatformService.saveAsset(PlatSaveAssetIn.builder()
                .authIn(authIn)
                .assetIn(SiSaveAssetIn.builder()
                        .platformId(asset.getTargetId())
                        .brief(brief)
                        .build())
                .build());
    }

    private void checkAsset(SiAssetBrief brief) {
        if (null == brief.getParentId()) {
            brief.setParentId("");
        }

        if (StrUtil.isBlank(brief.getType())) {
            throw CenterErrorCode.ASSET_TYPE_BLANK.buildException();
        }
    }

    /**
     * 把资源移动到目标资源下（改变其父节点）
     */
    public void move(UserAuthIn authIn, PlatAssetMoveIn req) {
        List<SiAssetBrief> assets;
        assets = siPlatformQueryService.findAssetList(AssetListInfo.builder()
                .platformId(req.getTargetId())
                .assetType(req.getAssetType())
                .assetIds(Collections.singletonList(req.getAssetId()))
                .build());
        if (null != assets && assets.size() == 1 && null != req.getParentId()) {
            SiAssetBrief assetBrief = assets.get(0);
            if (!req.getParentId().equals(assetBrief.getParentId())) {
                assetBrief.setParentId(req.getParentId());
                siPlatformService.saveAsset(PlatSaveAssetIn.builder()
                        .authIn(authIn)
                        .assetIn(SiSaveAssetIn.builder()
                                .platformId(req.getTargetId())
                                .tenantId("")
                                .brief(assetBrief)
                                .build())
                        .build());
            }
        }
    }

    public void checkAssetDefault(String id) {
        if (AuthConst.DEFAULT_ID.equals(id)) {
            throw CenterErrorCode.ASSET_DONT_DEL_DEFAULT_ASSET.buildException();
        }
    }

    public void delete(UserAuthIn authIn, PlatAssetDeleteIn asset) {
        checkAssetDefault(asset.getAssetId());

        if (AuthConst.REALM_SERVER.equals(asset.getServer())) {
            deleteRealmAsset(asset);
        } else {
            deletePlatAsset(authIn, asset);
        }
    }

    public void deleteRealmAsset(PlatAssetDeleteIn asset) {
        SiUserGroup userGroup;
        userGroup = siUserQueryService.lookupUserGroup(UserGroupLookupIn.builder()
                .realmId(asset.getTargetId())
                .groupId(asset.getAssetId())
                .build());
        if (null != userGroup) {
            userGroup.setState(SiAssetState.AT_DELETED);
            siUserService.saveUserGroup(UserGroupSaveIn.builder()
                    .realmId(asset.getTargetId())
                    .group(userGroup)
                    .build());
        }
    }

    public void checkDefaultPlatform(String platformId) {
        if (AuthConst.SSO_MANAGER_PLATFORM_ID.equals(platformId)) {
            throw CenterErrorCode.CANT_EDIT_DEFAULT_PLATFORM.buildException();
        }
    }

    /**
     * 删除资源
     */
    public void deletePlatAsset(UserAuthIn authIn, PlatAssetDeleteIn asset) {
        SiAssetBrief assetBrief;
        SiAssetLookupIn lookupIn = SiAssetLookupIn.builder()
                .ownerId(asset.getTargetId())
                .assetId(asset.getAssetId())
                .assetType(asset.getAssetType())
                .build();

        // 检测默认平台
        checkDefaultPlatform(asset.getTargetId());

        assetBrief = siPlatformQueryService.lookupAssetBrief(lookupIn);

        if (null != assetBrief) {
            Long count = siPlatformQueryService.getSubAssetCount(lookupIn);

            if (null != count && count > 0) {
                throw CenterErrorCode.ASSET_REMOVE_SUB_ASSET.buildException();
            }

            assetBrief.setState(SiAssetState.AT_DELETED);

            siPlatformService.saveAsset(PlatSaveAssetIn.builder()
                    .authIn(authIn)
                    .assetIn(SiSaveAssetIn.builder()
                            .platformId(asset.getTargetId())
                            .brief(assetBrief)
                            .build())
                    .build());
        } else {
            throw CenterErrorCode.ASSET_IS_DONT_EXIST.buildException();
        }
    }

    /**
     * 保存资源持有信息（比如保存分组下的权限信息）
     */
    public void saveAssetsOfHolder(UserAuthIn authIn, SiSaveHoldAssetsIn req) {
        siPlatformService.saveHoldAssets(PlatSaveHoldAssetsIn.builder()
                .authIn(authIn)
                .assetIn(SiSaveHoldAssets.builder()
                        .platformId(req.getTargetId())
                        .tenantId("")
                        .holdId(req.getHoldId())
                        .holdType(req.getHoldType())
                        .assetType(req.getAssetType())
                        .saveAll(req.getSaveAll())
                        .assets(req.getAssets())
                        .build())
                .build());
    }

    /**
     * 移除持有的资源（比如移除分组下的权限信息）
     */
    public void removeAssetsOfHolder(UserAuthIn authIn, SiSaveHoldAssetsIn req) {
        siPlatformService.removeRelAsset(PlatRemoveRelAssetIn.builder()
                .authIn(authIn)
                .relAsset(SiRemoveRelAsset.builder()
                        .platformId(req.getTargetId())
                        .assetId(req.getHoldId())
                        .assetType(req.getHoldType())
                        .targetType(req.getAssetType())
                        .targetIds(null != req.getAssets() ? req.getAssets().stream()
                                .map(SiSaveAssetVO::getAssetId)
                                .collect(Collectors.toList()) : null)
                        .build())
                .build());
    }

    /**
     * 保存资源持有信息（比如保存持有权限的所有资源信息（比如分组、角色、用户等））
     */
    public void saveHoldersOfAsset(UserAuthIn authIn, SiSaveAssetHoldsIn req) {
        siPlatformService.saveAssetHolds(PlatSaveAssetHoldsIn.builder()
                .authIn(authIn)
                .holdIn(SiSaveAssetHolds.builder()
                        .platformId(req.getTargetId())
                        .tenantId("")
                        .assetId(req.getAssetId())
                        .assetType(req.getAssetType())
                        .saveAll(req.getSaveAll())
                        .holdType(req.getHoldType())
                        .holders(req.getHolders())
                        .build())
                .build());
    }

    /**
     * 保存资源属性
     */
    public void saveAttributesOfAsset(UserAuthIn authIn, SaveAttributesOfAssetIn req) {
        if ("REALM_USER".equals(req.getAssetType())) {
            saveUserAttributes(authIn, req);
        } else if ("USER_GROUP".equals(req.getAssetType())) {
            saveUserGroupAttributes(req);
        } else if ("PLATFORM".compareToIgnoreCase(req.getServer()) == 0) {
            if ("PLATFORM".equals(req.getAssetType())) {
                savePlatformAttributes(authIn, req);
            } else {
                saveAssetAttributes(authIn, req);
            }
        } else {
            if ("REALM".equals(req.getAssetType())) {
                saveRealmAttributes(req);
            } else {
                saveAssetAttributes(authIn, req);
            }
        }
    }

    private void saveUserAttributes(UserAuthIn authIn, SaveAttributesOfAssetIn req) {
        SiUser siUser = siUserQueryService.lookupUserById(UserIdInfo.builder()
                .realmId(req.getTargetId())
                .userId(req.getAssetId())
                .build());
        if (null != siUser) {
            if (null == req.getProps()) {
                siUser.setProps(null);
            } else {
                siUser.setProps(SiProps.build(req.getProps()));
            }
            siUserService.saveUser(siUser);
        }
    }

    private void saveUserGroupAttributes(SaveAttributesOfAssetIn req) {
        SiUserGroup userGroup = siUserQueryService.lookupUserGroup(UserGroupLookupIn.builder()
                .realmId(req.getTargetId())
                .groupId(req.getAssetId()).build());
        if (null != userGroup) {
            if (null == req.getProps()) {
                userGroup.setProps(null);
            } else {
                userGroup.setProps(SiProps.build(req.getProps()));
            }
            siUserService.saveUserGroup(UserGroupSaveIn.builder()
                    .realmId(req.getTargetId())
                    .group(userGroup)
                    .build());
        }
    }

    private void saveRealmAttributes(SaveAttributesOfAssetIn req) {
        SiRealmDTO realm = siRealmQueryService.findRealm(req.getTargetId());
        if (null == realm) {
            throw CenterErrorCode.REALM_IS_DONT_EXIST.buildException();
        }

        if (null == req.getProps()) {
            realm.setProps(null);
        } else {
            realm.setProps(req.getProps());
        }

        siRealmService.saveRealm(realm);
    }

    private void savePlatformAttributes(UserAuthIn authIn, SaveAttributesOfAssetIn req) {
        SiPlatform platform = siPlatformQueryService.lookupPlatform(SiPlatIdFind.builder()
                .platformId(req.getTargetId()).build());
        if (null != platform && req.getProps() != null) {
            SiProps props = platform.getProps();
            if (null == props) {
                platform.setProps(SiProps.build(req.getProps()));
            } else {
                req.getProps().forEach(props::addProp);
            }
            siPlatformService.updatePlatform(PlatformSaveIn.builder()
                    .authIn(authIn).platform(platform).build());
        }
    }

    public void saveAssetAttributes(UserAuthIn authIn, SaveAttributesOfAssetIn req) {
        SiAssetBrief assetBrief;
        SiAssetEntry assetEntry = siPlatformQueryService.lookupAssetInfo(SiAssetId.builder()
                .ownerId(req.getTargetId())
                .assetId(req.getAssetId())
                .assetType(req.getAssetType())
                .build());

        if (null == assetEntry || null == assetEntry.getAsset()) {
            throw CenterErrorCode.ASSET_IS_DONT_EXIST.buildException();
        }

        assetBrief = BeanUtil.copyProperties(assetEntry.getAsset(), SiAssetBrief.class);
        if (null == req.getProps()) {
            assetBrief.setProps(SiProps.EMPTY);
        } else {
            assetBrief.setProps(SiProps.build(req.getProps()));
        }

        siPlatformService.saveAsset(PlatSaveAssetIn.builder()
                .authIn(authIn)
                .assetIn(SiSaveAssetIn.builder()
                        .platformId(req.getTargetId())
                        .brief(assetBrief)
                        .build())
                .build());
    }

    /**
     * 移除资源属性
     */
    public void removeAttributesOfAsset(UserAuthIn authIn, RemoveAttributesOfAssetIn req) {
        if ("REALM_USER".equals(req.getAssetType())) {
            removeUserAttributes(authIn, req);
        } else if ("USER_GROUP".equals(req.getAssetType())) {
            removeUserGroupAttributes(authIn, req);
        } else if ("platform".equals(req.getServer())) {
            if ("PLATFORM".equals(req.getAssetType())) {
                removePlatformAttributes(authIn, req);
            } else {
                removeAssetAttributes(authIn, req);
            }
        } else {
            if ("REALM".equals(req.getAssetType())) {
                removeRealmAttributes(authIn, req);
            } else {
                removeAssetAttributes(authIn, req);
            }
        }
    }

    public void removeUserAttributes(UserAuthIn authIn, RemoveAttributesOfAssetIn req) {
        siUserService.removeAttrOfUser(SiUserAttrRemoveIn.builder()
                .authIn(authIn)
                .realmId(req.getTargetId())
                .userId(req.getAssetId())
                .props(req.getProps())
                .build());
    }

    public void removeUserGroupAttributes(UserAuthIn authIn, RemoveAttributesOfAssetIn req) {
        siRealmService.removeAttrOfRealmGroup(SiRealmGroupAttrRemoveIn.builder()
                .authIn(authIn)
                .realmId(req.getTargetId())
                .groupId(req.getAssetId())
                .props(req.getProps())
                .build());
    }

    public void removePlatformAttributes(UserAuthIn authIn, RemoveAttributesOfAssetIn req) {

    }

    public void removeRealmAttributes(UserAuthIn authIn, RemoveAttributesOfAssetIn req) {
        siRealmService.removeAttrOfRealm(SiRealmAttrRemoveIn.builder()
                .authIn(authIn)
                .realmId(req.getTargetId())
                .props(req.getProps())
                .build());
    }

    public void removeAssetAttributes(UserAuthIn authIn, RemoveAttributesOfAssetIn req) {
        siPlatformService.removeAttrOfAsset(PlatRemoveAssetAttributeIn.builder()
                .authIn(authIn)
                .attrIn(SiRemoveAttrOfAssetIn.builder()
                        .platformId(req.getTargetId())
                        .assetId(req.getAssetId())
                        .assetType(req.getAssetType())
                        .props(req.getProps())
                        .build())
                .build());
    }
}
